#pragma once
#include "base.hpp"
#include "fields.hpp"
#include "my_tool.hpp"
#include "message.hpp"

#include <iostream>
#include <string>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h> // 客户端不要直接loop, 否者死循环(创建线程去)
#include <muduo/net/TcpServer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/TcpConnection.h>   // 通过这个conn发送数据
#include <muduo/base/CountDownLatch.h> // 避免客户端连接没建立完, 就发送数据
#include <unordered_map>
#include <mutex>
#include <unordered_map>


// MuduoBuffer --> LVProtocol -> MuduoConnection --> MuduoServer --> MuduoClient
namespace jsonrpc
{
    // Muduo库已经提供 -- 直接调用
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf) {}

        // 能从缓冲区读多少字节
        virtual size_t readableBytes() override
        {
            return _buf->readableBytes();
        }
        // 读4个字节的数据, 不会从缓冲区中拿出来
        virtual int32_t peekInt32() override
        {
            return _buf->peekInt32();
        }
        // 拿出4个字节
        // muduo库是一个网络库, 从缓冲区取出4字节整型, 会进行网络字节序的转换
        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }
        // 跳过4个字节
        virtual void retrieveInt32() override
        {
            return _buf->retrieveInt32();
        }
        // 根据长度取出指定数据
        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf; // 指向connection里面的缓冲区, 不能随意释放
    };

    // 工厂
    class BufferFactory {
    public:
        template<typename ...Args>
        static BaseBuffer::ptr create(Args&& ...args) {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };


    //  4        4        4
    // Length   MType  IDLength  MID  Body
    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;

        // 是否能处理数据(是否完整)
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override
        {
            if (buf->readableBytes() < lenFieldLength)
            {
                return false;
            }
            int32_t total_len = buf->peekInt32();
            // DLOG("total_len:%d", total_len);
            if (buf->readableBytes() < (total_len + lenFieldLength))
            {
                return false;
            }
            return true;
        }

        // 从buff中读取出一条完整消息, 并进行反序列化
        virtual bool onMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) override
        {
            int32_t total_len = buf->readInt32();    // 总长度
            MType my_type = (MType)buf->readInt32(); // 消息类型
            int32_t id_len = buf->readInt32();       // id长度
            std::string id = buf->retrieveAsString(id_len);
            int32_t body_len = total_len - mtypeFieldLength - idFieldLength - id_len; // body长度
            std::string body = buf->retrieveAsString(body_len);

            // 2.根据消息构造对象 + 填充信息
            msg = MessageFactory::create(my_type);
            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误, 构造对象失败");
                return false;
            }

            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息反序列化失败");
                return false;
            }

            msg->setId(id);
            msg->setMType(my_type);
            return true;
        }

        // 将消息序列化
        virtual std::string serialize(const BaseMessage::ptr &msg)
        {
            // Length   MType  IDLength  MID  Body
            std::string body = msg->serialize();
            std::string id = msg->rid();
            // 因为muduo库是网络通信(读取数据会进行网络字节序的转换, 我们填消息也要进行转换)
            auto mtype = htonl((int32_t)msg->rMType());
            int32_t id_len = htonl(id.size());
            int32_t h_total_len = mtypeFieldLength + idFieldLength + id.size() + body.size(); // 保留一下
            int32_t n_total_len = htonl(h_total_len);
            // DLOG("h_total_len : %d", h_total_len);

            std::string result;
            result.reserve(h_total_len);
            result.append((char *)&n_total_len, lenFieldLength);
            result.append((char *)&mtype, mtypeFieldLength);
            result.append((char *)&id_len, idFieldLength);
            result.append(id);
            result.append(body);

            return result;
        }

    private:
        const size_t lenFieldLength = 4;
        const size_t mtypeFieldLength = 4;
        const size_t idFieldLength = 4;
    };

    // 工厂生成对象
    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static jsonrpc::BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _conn(conn), _protocol(protocol)
        {
        }

        virtual void send(const BaseMessage::ptr &msg) override
        {
            std::string body = _protocol->serialize(msg);
            _conn->send(body);
        }

        virtual void shutdown() override
        {
            _conn->shutdown();
        }

        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::ptr _protocol;        // 对消息进行序列化
        muduo::net::TcpConnectionPtr _conn; // 发送消息
    };

    class ConnectionFactory 
    {
    public:
        template<typename ...Args>
        static BaseConnection::ptr create(Args&& ...args) 
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    // ---------------------------------MuduoServer---------------------------------
    class MuduoServer : public jsonrpc::BaseServer
    {
    public:
        using ptr = std::shared_ptr<BaseServer>;
        // loop + server
        MuduoServer(int port)
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "DictServer", muduo::net::TcpServer::kReusePort), _protocol(ProtocolFactory::create())
        {
            // 设置连接事件（连接建立/管理）的回调
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            // 设置连接消息的回调
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        }

        virtual void start() override
        {
            _server.start();  // 先开始监听
            _baseloop.loop(); // 开始死循环事件监控
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            // 封装连接并管理, 连接建立, 添加连接, + 调用对于回调
            if (conn->connected())
            {
                std::cout << "连接建立！\n";
                auto muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
                if (_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else
            {
                std::cout << "连接断开\n";
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        return;
                    }
                    else
                    {
                        muduo_conn = it->second;
                        _conns.erase(conn);
                    }
                    if (_cb_close)
                        _cb_close(muduo_conn);
                }
            }
        }

        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来，开始处理！");
            auto base_buf = BufferFactory::create(buf);
            while(1) 
            {
                if (_protocol->canProcessed(base_buf) == false) {
                    //数据不足
                    if (base_buf->readableBytes() > maxDataSize) {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大！");
                        return ;
                    }
                    DLOG("数据量不足！");
                    break;
                }
                DLOG("缓冲区中数据可处理！");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false) {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误！");
                    return ;
                }
                DLOG("消息反序列化成功！")
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end()) {
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }
                DLOG("调用回调函数进行消息处理！");
                if (_cb_message) _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16); // 64kb
        BaseProtocol::ptr _protocol;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };

    class ServerFactory {
    public:
        template<typename ...Args>
        static BaseServer::ptr create(Args&& ...args) {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    // ---------------------------------MuduoClient---------------------------------
    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int sport):
            _protocol(ProtocolFactory::create()),
            _baseloop(_loopthread.startLoop()),
            _downlatch(1),
            _client(_baseloop, muduo::net::InetAddress(sip, sport), "MuduoClient")
        {}
        
        // 连接服务器
        virtual void connect() override
        {
            //设置连接事件（连接建立/管理）的回调
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            //设置连接消息的回调
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this, 
                std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            //连接服务器
            _client.connect();
            _downlatch.wait();
        }

        // 连接是否建立
        virtual bool connected() override{return _conn && _conn->connected();}  
        // 关闭连接
        virtual void shutdown() override{ _client.disconnect();}     
        // 获取连接
        virtual BaseConnection::ptr connection() override{ return _conn;}  
        // 发送消息 
        virtual bool send(const BaseMessage::ptr& msg) override
        {
            if (connected() == false) {
                ELOG("连接已经断开，发送数据失败");
                return false;
            }
            _conn->send(msg);
        }  

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn) {
            if (conn->connected()) 
            {
                DLOG("连接建立！");
                _downlatch.countDown();//计数--，为0时唤醒阻塞
                _conn = ConnectionFactory::create(conn, _protocol);
            }
            else 
            {
                DLOG("连接断开！");
                _conn.reset();
            }
        }

        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp){
            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                // 检查数据是否完整
                if (_protocol->canProcessed(base_buf) == false)
                {
                    ELOG("缓冲区中不够一条完整数据");
                    // 判断缓冲区中数据大小是否超过我们限制的最大大小
                    if (base_buf->readableBytes() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大");
                        return;
                    }
                    break;
                }
                // 获取一条完整数据
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }

                // 调用回调函数, 进行处理
                if(_cb_message)_cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16); // 64kb
        BaseProtocol::ptr _protocol;
        BaseConnection::ptr _conn;
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client;
    };

    class ClientFactory {
    public:
        template<typename ...Args>
        static BaseClient::ptr create(Args&& ...args) {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}